/*
 * Copyright 2006 Sun Microsystems, Inc.  All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Sun Microsystems nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/* *
 * Originally from:
 * http://blogs.sun.com/andreas/resource/InstallCert.java
 * Use:
 * java InstallCert hostname
 * Example:
 * % java InstallCert ecc.fedora.redhat.com
 */
package yfdc;
import javax.net.ssl.*;
import java.io.*;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/**
 * Class used to add the server's certificate to the KeyStore
 * with your trusted certificates.
 */
public final class InstallCert {
	private static final String file_name = ("."+ File.separatorChar+"jssecacerts");
	private static final char[] HEXDIGITS = "0123456789ABCDEF".toCharArray();
	private static final Object lock = (new Object());
	private InstallCert() {
		throw (new UnsupportedOperationException("could not be implied"));
	}
	public static void main(final String[] args) {
		boolean kontinue = true;
		String[] margs = args;
		if (margs == null) {
			margs = new String[1];
			margs[0] = "";
		}
		for (String i : margs) {
			if ("gradle".equals(i)) {
				(new Thread(() -> { try { InstallCert.calk(new String[]{"maven.aliyun.com", "changeit", "gradle"}); } catch (Throwable ignore){} })).start();
				(new Thread(() -> { try { InstallCert.calk(new String[]{"jcenter.bintray.com", "changeit", "gradle"}); } catch (Throwable ignore){} })).start();
				(new Thread(() -> { try { InstallCert.calk(new String[]{"maven.google.com", "changeit", "gradle"}); } catch (Throwable ignore){} })).start();
				kontinue = false;
				break;
			}
		}
		if (kontinue) {
			try {
				calk(args);
			} catch (Throwable exception) {
				System.out.println("程序发生错误:");
				System.out.println(exception.toString());
				System.out.println(exception.getMessage());
				System.out.println("使用--verbose显示详细信息。");
				for (String i : margs) {
					if ("--verbose".equalsIgnoreCase(i)) {
						exception.printStackTrace(System.out);
						break;
					}
				}
			} finally {
				System.exit(0);
			}
		}
	}
	private synchronized static void calk(final String[] args) throws Exception {
		synchronized (InstallCert.lock) {
			String host = null;
			int port = 0;
			char[] passphrase = null;
			boolean gradle = false;
			if (args == null) return;
			if ((args.length == 1) || (args.length == 2)) {
				String[] c = args[0].split(":");
				host = c[0];
				port = (c.length == 1) ? 443 : Integer.parseInt(c[1]);
				String p = (args.length == 1) ? "changeit" : args[1];
				passphrase = p.toCharArray();
			} else if (args.length == 3) {
				String[] c = args[0].split(":");
				host = c[0];
				port = (c.length == 1) ? 443 : Integer.parseInt(c[1]);
				String p = args[1];
				passphrase = p.toCharArray();
				if ("gradle".equals(args[2])) gradle = true;
			} else {
				System.out.println("使用方法: java InstallCert <主机名称>[:端口号] [密码] [gradle]");
				return;
			}
			File file = new File(file_name);
			if (!file.isFile()) {
				char SEP = File.separatorChar;
				File dir = new File(System.getProperty("JAVA_HOME") + SEP
						+ "lib" + SEP + "security");
				file = new File(dir, file_name);
				if (!file.isFile()) {
					file = new File(dir, file_name);
				}
			}
			System.out.println("正在加载密钥库 " + file + "...");
			InputStream in = new java.io.FileInputStream(file);
			KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
			ks.load(in, passphrase);
			in.close();
			SSLContext context = SSLContext.getInstance("TLS");
			TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
			tmf.init(ks);
			X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
			SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
			context.init(null, new TrustManager[]{tm}, null);
			SSLSocketFactory factory = context.getSocketFactory();
			System.out.println("正在连接 " + host + ":" + port + "...");
			try {
				SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
				socket.setSoTimeout(10000);
				System.out.println("正在开始SSL握手...");
				socket.startHandshake();
				socket.close();
				System.out.println();
				System.out.println("没有找到错误, 证书已被信任。");
			} catch (Throwable e) {
				System.out.println("[错误]:" + e.toString());
				System.out.println(e.getMessage());
				//e.printStackTrace(System.out); //fucking line:to many logs, which are helpless for user.
			}
			X509Certificate[] chain = tm.chain;
			if (chain == null) {
				System.out.println("无法找到服务器的证书链。");
				return;
			}
			BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
			System.out.println();
			System.out.println("服务器发来了 " + chain.length + " 个证书:");
			System.out.println();
			MessageDigest sha1 = MessageDigest.getInstance("SHA1");
			MessageDigest md5 = MessageDigest.getInstance("MD5");
			for (int i = 0; i < chain.length; i++) {
				X509Certificate cert = chain[i];
				System.out.println(" " + (i + 1) + " Subject " + cert.getSubjectDN());
				System.out.println("   Issuer  " + cert.getIssuerDN());
				sha1.update(cert.getEncoded());
				System.out.println("   sha1    " + toHexString(sha1.digest()));
				md5.update(cert.getEncoded());
				System.out.println("   md5     " + toHexString(md5.digest()));
				System.out.println();
			}
			System.out.println("输入要添加的证书编号, 或者按\'q\'退出:[1]");
			String line;
			try {
				line = reader.readLine().trim();
			} catch (Throwable w) {
				line = gradle ? "1" : "q";
			}
			int k = 0;
			try {
				k = (line.length() == 0) ? 0 : Integer.parseInt(line) - 1;
			} catch (Throwable e) {
				System.out.println("密钥库没有改变。");
				return;
			}
			X509Certificate cert = chain[k];
			String alias = host + "-" + (k + 1);
			ks.setCertificateEntry(alias, cert);
			OutputStream out = new FileOutputStream(file);
			ks.store(out, passphrase);
			out.close();
			System.out.println();
			System.out.println(cert);
			System.out.println();
			System.out.println("成功将证书'" + alias + "'存储在'" + file.getAbsolutePath() + "'密钥库中。");
		}
	}
	private static String toHexString(byte[] bytes) {
		StringBuilder sb = new StringBuilder(bytes.length * 3);
		for (int b : bytes) {
			b &= 0xff;
			sb.append(HEXDIGITS[b >> 4]);
			sb.append(HEXDIGITS[b & 15]);
			sb.append(' ');
		}
		return sb.toString();
	}
	private static final class SavingTrustManager implements X509TrustManager {
		private final X509TrustManager tm;
		private X509Certificate[] chain;
		private SavingTrustManager() {
			throw (new UnsupportedOperationException("could not be implied."));
		}
		SavingTrustManager(final X509TrustManager _tm) {
			this.tm = _tm;
		}
		public X509Certificate[] getAcceptedIssuers() {
			/* *
			 * This change has been done due to the following resolution advised for Java 1.7+
			 http://infposs.blogspot.kr/2013/06/installcert-and-java-7.html
			 **/
			return new X509Certificate[0];
			//throw new UnsupportedOperationException();
		}
		public void checkClientTrusted(X509Certificate[] chain, String authType)
				throws CertificateException {
			throw new UnsupportedOperationException();
		}
		public void checkServerTrusted(final X509Certificate[] _chain, final String _authType)
				throws CertificateException {
			this.chain = _chain;
			this.tm.checkServerTrusted(_chain, _authType);
		}
	}
}
